package it.polimi.dei.SeCo.YAGO;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import yago2.normalize.Normalize;

/**
 * @author Simone Benefico
 */
public class YAGOTool {
	
	private Connection conn = null;
	
	/**
	 * Gets a new Connection to use to access the db
	 * @param srvName name or IP of the server
	 * @param dbName name of the database
	 * @param user username used to access connect to the db
	 * @param password password used to access connect to the db
	 * @param port open port of the server listening for db connections
	 */
	public YAGOTool(String srvName, String dbName, String user, String password, int port) {
		DBAccess access = new DBAccess(srvName, dbName, user, password, port);
		conn = access.getConnection();	
	}
	
	/**
	 * Returns the arg2 of the facts with the other attributes is equal to the given ones.
	 * @param arg1 arg1 to find in YAGO2
	 * @param relation relation to find in YAGO2
	 * @return list of arg2 found in YAGO2
	 */
	public List<String> getFacts(String arg1, String relation) {
		if(arg1 != null)
			arg1 = Normalize.entity(arg1);
		List<String> facts = null;
		try {
			Statement stmt = conn.createStatement();
			ResultSet rs = null;
			if(relation==null || relation.isEmpty())
				return null;
			rs = stmt.executeQuery("select * from facts where (arg1='"+arg1+"' and relation='"+relation+"')");
			facts = new ArrayList<String>();
			while(rs.next())
				if(!facts.contains(Normalize.unEntity(rs.getString("arg2"))))
					facts.add(Normalize.unEntity(rs.getString("arg2")));
			rs.close();
			stmt.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		if(facts==null || facts.isEmpty())
			return null;
		return facts;
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>hasPreferredMeaning</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 arg1 to find in YAGO2 where relation is equal to <i>hasPreferredMeaning</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>hasPreferredMeaning</i> and arg1 is equal to <i>arg1</i>
	 */
	public List<String> getPreferredMeanings(String arg1) {
		return getFacts(arg1, "hasPreferredMeaning");
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>means</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>means</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>means</i> and arg1 is equal to <i>arg1</i>
	 */
	public List<String> getMeanings(String arg1) {
		return getFacts(arg1, "means");
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>isLocatedIn</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>isLocatedIn</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>isLocatedIn</i> arg1 is equal to <i>arg1</i>
	 */
	public String getWhereArg1IsLocated(String arg1) {
		List<String> isLocatedIn = getFacts(arg1, "isLocatedIn");
		if(isLocatedIn!=null && !isLocatedIn.isEmpty())
			return isLocatedIn.get(0);
		return null;
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>type</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>type</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>type</i> and arg1 is equal to <i>arg1</i>
	 */
	public List<String> getType(String arg1) {
		return getFacts(arg1, "type");
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>hasPreferredName</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>hasPreferredName</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>hasPreferredName</i> and arg1 is equal to <i>arg1</i>
	 */
	public String getPreferredName(String arg1) {
		List<String> listOfPreferredNames = getFacts(arg1,"hasPreferredName");
		if(listOfPreferredNames!=null && !listOfPreferredNames.isEmpty())
			return listOfPreferredNames.get(0);
		return null;
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>hasGeoNamesId</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>hasGeoNamesId</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>hasGeoNamesId</i> and arg1 is equal to <i>arg1</i>
	 */
	public String getGeoNamesId(String arg1) {
		List<String> listOfGeoNamesId = getFacts(arg1,"hasGeoNamesId");
		if(listOfGeoNamesId!=null && !listOfGeoNamesId.isEmpty())
			return listOfGeoNamesId.get(0);
		return null;
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>hasGeoCoordinates</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>hasGeoCoordinates</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>hasGeoCoordinates</i> and arg1 is equal to <i>arg1</i>
	 */
	public String getGeoCoordinates(String arg1) {
		List<String> listOfGeoCoordinates = getFacts(arg1,"hasGeoCoordinates");
		if(listOfGeoCoordinates!=null && !listOfGeoCoordinates.isEmpty())
			return listOfGeoCoordinates.get(0);
		return null;
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>hasLatitude</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>hasLatitude</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>hasLatitude</i> and arg1 is equal to <i>arg1</i>
	 */
	public Double getLatitude(String arg1) {
		List<String> listOfLatitudes = getFacts(arg1, "hasLatitude");
		if(listOfLatitudes!=null && !listOfLatitudes.isEmpty())
			return Double.parseDouble(Normalize.unNumber(listOfLatitudes.get(0)));
		return null;
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>hasLongitude</i> arg1 is equal to <i>arg1</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>hasLongitude</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>hasLongitude</i> and arg1 is equal to <i>arg1</i>
	 */
	public Double getLongitude(String arg1) {
		List<String> listOfLongitudes = getFacts(arg1, "hasLongitude");
		if(listOfLongitudes!=null && !listOfLongitudes.isEmpty())
			return Double.parseDouble(Normalize.unNumber(listOfLongitudes.get(0)));
		return null;
	}
	
	/**
	 * Finds facts from YAGO2 where relation is equal to <i>type</i> arg1 is like <i>geoclass_%</i>.
	 * @param arg1 <i>arg1</i> to find in YAGO2 where relation is equal to <i>type</i>
	 * @return list of facts from YAGO2 where relation is equal to <i>type</i> and arg1 is like <i>geoclass_%</i>
	 */
	public String getGeoTypes(String arg1) {
		if(arg1 != null)
			arg1 = Normalize.entity(arg1);
		List<String> geoTypes = null;
		try {
			Statement stmt = conn.createStatement();
			ResultSet rs = conn.createStatement().executeQuery("select * from facts where (arg1='"+arg1+"' and relation='type' and arg2 like 'geoclass_%')");
			geoTypes = new ArrayList<String>();
			while(rs.next())
				if(!geoTypes.contains(rs.getString("arg2")))
					geoTypes.add(rs.getString("arg2"));
			rs.close();
			stmt.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		if(geoTypes!=null && !geoTypes.isEmpty())
			return Normalize.unGeonamesClass(geoTypes.get(0));
		else return null;
	}
	
	/**
	 * Finds the significant meanings of a query (in this case, a location) from facts where relation is equal to <i>hasPreferredMeaning</i> or <i>means</i>.
	 * @param query location which the meaning stands for
	 * @return list of significant meanings
	 */
	public List<String> getSignificantMeanings(String query) {
		if(query != null)
			query = Normalize.entity(query);
		List<String> meanings = null;
		List<String> preferredMeanings = getPreferredMeanings(query);
		if(preferredMeanings!=null && !preferredMeanings.isEmpty())
			return preferredMeanings;
		else {
			meanings = getMeanings(query);
			if(meanings!=null && !meanings.isEmpty())					
				return meanings;
		}
		return null;
	}
	
	/**
	 * Creates a GeoEntity with the information found in YAGO2
	 * @param arg1 name of the GeoEntity to create
	 * @return created GeoEntity
	 */
	public GeoEntity getGeoEntity(String arg1) {
		String geoType = getGeoTypes(arg1);
		String geoNamesId = getGeoNamesId(arg1);
		String isLocatedIn = getWhereArg1IsLocated(arg1);
		Double latitude = getLatitude(arg1);
		Double longitude = getLongitude(arg1);
		if((geoType==null || geoType.isEmpty()) && (geoNamesId==null || geoNamesId.isEmpty()) && (isLocatedIn==null || isLocatedIn.isEmpty()) && latitude==null && longitude==null)
			return null;
		return new GeoEntity(arg1, geoType, geoNamesId, isLocatedIn, latitude, longitude);
	}
}
